package liming.tool.log;

import liming.tool.lock.CodeLock;
import liming.tool.pool.ObjectsPool;
import liming.tool.pool.PoolPolicy;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class LogPool {
    public static final ObjectsPool<LogMessage> LOG_MESSAGE_OBJECTS_POOL = new ObjectsPool<>(new CodeLock(),new ArrayList<>(),1000, PoolPolicy.PutPolicy.REMOVE_OLDEST,PoolPolicy.TakePolicy.BLOCK);

    //stackTraceElement time name message
    //message
    //name message
    //time message
    //time name message
    //stackTraceElement message
    //stackTraceElement name message
    //stackTraceElement time message
    //stackTraceElement time name message

    //stackTraceElement time message 0
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");
    private static final Map<String, String> mappingClassName = new ConcurrentHashMap<>();

    public static String formatTime(){
        return LocalDateTime.now().format(formatter);
    }

    /**
     * 获取调用这个方法的信息，并返回一个LogMessage对象
     */
    public static LogMessage log(Object message) {
        return logTime(getStackTraceElement(), LocalDateTime.now().format(formatter), message);
    }

    public static LogMessage logTime(String time, Object message) {
        return logTime(getStackTraceElement(), time, message);
    }

    public static LogMessage logName(String name, Object message) {
        return logName(getStackTraceElement(), name, message);
    }

    public static LogMessage log(String time, String name, Object message) {
        return log(getStackTraceElement(), time, name, message);
    }

    public static LogMessage log(StackTraceElement stackTraceElement, Object message) {
        return logTime(stackTraceElement, LocalDateTime.now().format(formatter), message);
    }

    public static LogMessage logTime(StackTraceElement stackTraceElement, String time, Object message) {
        return getLogMessage(stackTraceElement, time, message);
    }

    public static LogMessage logName(StackTraceElement stackTraceElement, String name, Object message) {
        return log(stackTraceElement, LocalDateTime.now().format(formatter), name, message);
    }

    public static LogMessage log(StackTraceElement stackTraceElement, String time, String name, Object message) {
        LogMessage logMessage = getLogMessage(stackTraceElement, time, message);
        logMessage.name = name;
        return logMessage;
    }

    private static LogMessage getLogMessage(StackTraceElement stackTraceElement, String time, Object message) {
        LogMessage logMessage = new LogMessage(stackTraceElement, time, message);
        LOG_MESSAGE_OBJECTS_POOL.put(logMessage);
        return logMessage;
    }

    /**
     * 0为getStackTrace方法位置，1为当前位置，2为上一级位置，及调用getStackTraceElement的位置，3为调用 调用getStackTraceElement方法 的方法的位置
     */
    public static StackTraceElement getStackTraceElement() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        return stackTrace[stackTrace.length > 3 ? 3 : stackTrace.length - 1];
    }
    public static StackTraceElement getStackTraceElement(int index) {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        return stackTrace[stackTrace.length > index ? index : stackTrace.length - 1];
    }

    /**
     * 将调用者的类名进行处理后缓存，减少类名获取的开销
     *
     * @param name 类名
     * @return 调用者类名
     */
    private static String getName(String name) {
        return mappingClassName.computeIfAbsent(name, k -> {
            try {
                Class<?> aClass = Class.forName(k);
                String simpleName = aClass.getSimpleName();
                return simpleName.isEmpty() ? aClass.getName() : simpleName;
            } catch (ClassNotFoundException e) {
                return k;
            }
        });
    }

    public static List<LogMessage> getLogs(GetName name){
        List<LogMessage> logs =new ArrayList<>();
        LOG_MESSAGE_OBJECTS_POOL.getList().stream().filter(logMessage -> name.names.contains(logMessage.name)).forEach(logs::add);
        return logs;
    }
    public static List<LogMessage> getLogs(RemoveName name){
        List<LogMessage> logs =new ArrayList<>();
        LOG_MESSAGE_OBJECTS_POOL.getList().stream().filter(logMessage -> !name.names.contains(logMessage.name)).forEach(logs::add);
        return logs;
    }

    public static class GetName{
        Set<String> names=new HashSet<>();
        public GetName add(String... names){
            this.names.addAll(Arrays.asList(names));
            return this;
        }
    }

    public static class RemoveName{
        Set<String> names = new HashSet<>();
        public RemoveName remove(String... names){
            this.names.addAll(Arrays.asList(names));
            return this;
        }
    }

    public enum Color {
        // ANSI转义码常量
        RESET("\u001B[0m"),// 重置
        BLACK("\u001B[30m"),// 黑色
        RED("\u001B[31m"),// 红色
        GREEN("\u001B[32m"),// 绿色
        YELLOW("\u001B[33m"),// 黄色
        PURPLE("\u001B[35m"),// 紫色
        CYAN("\u001B[36m"),// 青色
        WHITE("\u001B[37m");// 白色

        private final String color;

        Color(String color) {
            this.color = color;
        }

        @Override
        public String toString() {
            return color;
        }
    }

    public static class LogMessage {
        static LogFormat defaultFormat=(message)-> Color.GREEN +" "+ message.getTime() + " |"
                + Color.CYAN +" "+ message.getName() + " |"
                + Color.GREEN+" " + message.getClassName() + "." + message.getMethodName() + ":" + message.getLineNumber() + " |"
                + Color.RESET+" " + message.getMessage();
        LogFormat format=defaultFormat;
        String time;
        String name;
        Object message;

        String className;
        String simpleName;
        String methodName;
        int lineNumber;

        LogMessage(StackTraceElement stackTraceElement, String time, Object message) {
            this.time = time;
            this.message = message;
            name = simpleName = LogPool.getName(className = stackTraceElement.getClassName());
            methodName = stackTraceElement.getMethodName();
            lineNumber = stackTraceElement.getLineNumber();
        }

        public String getTime() {
            return time;
        }

        public String getClassName() {
            return className;
        }

        public String getMethodName() {
            return methodName;
        }

        public int getLineNumber() {
            return lineNumber;
        }

        public String getSimpleName() {
            return simpleName;
        }

        public String getName() {
            return name;
        }

        public Object getMessage() {
            return message;
        }

        //设置格式
        public static void setDefaultFormat(LogFormat format) {
            defaultFormat=format;
        }
        public static LogFormat getDefaultFormat() {
             return defaultFormat;
        }
        public void setFormat(LogFormat format) {
            this.format=format;
        }
        public String format() {
            return format.format(this);
        }

        @Override
        public String toString() {
            return format();
        }
    }
    @FunctionalInterface
    public interface LogFormat {
        String format(LogMessage logMessage);
    }
}
